Unit Test
One of the most commonly skipped or minimally managed areas of application development is unit testing. It is often seen as overhead, even unnecessary, and given very little or no attention. On some projects where requirements are often changing rapidly, the developer can get frustrated trying to keep up with the changes and refactoring their unit tests. However, there are numerous articles and case studies which show that when investing in high code quality up front (by implementing correct unit tests and testing practices) will have exponential cost savings in the long term. And though not explicitly described in the Agile Manifesto, unit testing is a crucial part of agile development in delivering high quality products. It is commonly part of the automated build and delivery process.
This post goes over the basics of setting up Unit Tests using the built-in test project available in Visual Studio for both MVC and WebAPI applications. The example below uses Dependency Injection (using Unity) and has a couple of sample unit tests for the API controllers.
Sample Project
The sample project is an extension of the Widgets.API project found here:
https://github.com/johnlee/Widgets.Tests
The data repository has been separated into a separate project. Here, the repository uses interfaces so that the actual data source can be injected during runtime. This is done using dependency injection.
For example, the IOrderRepository interface defines the methods needed to interact with Order data. This, with the IWidgetRepository are part of the main IRepository, which is declared in the API Controllers. You can see the injection point on the BaseController’s constructor for this Repository interface:
public class BaseController : ApiController
{
IRepository _repository;
ModelFactory _factory;
public BaseController(IRepository repository)
{
_repository = repository; // getting the repository container using dependency injection
}
The container used for this example is UnityContainer, which is available on NuGet under Microsoft.Practices.Unity. The registration for this container can be found under the App_Start folder in UnityConfig.cs.
public static void RegisterComponents()
{
var container = new UnityContainer();
// Injecting the Repository from Widgets.Data
container.RegisterType<IRepository, Repository>(); // Using the MockRepository since we dont have a real database
GlobalConfiguration.Configuration.DependencyResolver = new UnityDependencyResolver(container);
}
The registration is done during the start of the application, and is called from the main Global.asax’s Application_Start() method.
Having the API controllers accept IRepository interfaces, we can inject MockRepositories during unit testing. The unit tests are found in the separate project, which Visual Studio will scaffold for you when you create a new “Test Project”.
Under the Mocks folder, there are the MockRepositories, as well as mock data that is used during the unit tests. The unit tests in this example are for the two API controllers. It is to test the GET, PUT, POST and DELETE methods of these controllers.
TBD
Also in the Mocks folder is a MockHttpConfiguration class and MockUser class. These classes are needed to mockup the HTTP environment, which does not exist when running unit tests. The MockHttpConfiguration class will mock a fake HTTP Request, whereas the MockUser will create a mock GenericIdentity object with a couple of mock claims.
TBD
When writing unit tests, we follow a couple of different conventions. First, the test classes should only test a specific class or function of the application. So in this example, we have two test class for each of the two API controllers. Next, the test cases in these test classes should be named with the following convention:
[Method being Tested]_[Condition for Method]_[Expect result from Method]
With this naming convention it should be fairly easy to determine what each test is testing. Also, which each of the test cases it should clearly list out the 3 A’s that compose a unit test. Those are:
Arrange – setting up the test Act – perform the test Assert – the result of test, which is verified for pass or fail
Test Driven Development (TDD)
Test driven development is where requirements are written as runnable unit tests before the actual design or implementation of the application. All these unit tests would fail at first, but as the application is being developed, the unit tests would pass thereby indicating that requirement has been completed.
TBD
Integration Tests
Integration tests, as the name applies, is a form of testing that is done at a higher level than unit testing. Whereas unit tests focus on specific piece of functionality, integration tests often test a set of functionalities to make sure they are all interacting as expected. This often requires the following:
- Ability to persist data
- Ability to interact with different components of the application, either through code or UI
- TBD
Microsoft Fakes
Starting in .Net 4.0 and Visual Studio 2013 and higher, Microsoft has introduced the concept of Fakes. Fakes are mock data that can be inserted into application code to mimic a certain behavior for testing. This is done by the use of Shims and Stubs.
Shims are functions or objects used to replace some other object or assembly. It is often used to intercept assemblies that are being referenced from outside the core application assemblies. For example, Shims can be used to intercept calls to the DateTime class so that the returned time values are controlled for testing.
Stubs are functions or objects that can replace other functions or objects in the core of the application. This is what is being used in this sample project where the Repository object is being stubbed with a MockRepository. This is done by using interfaces. In this sample project the mock repository was being injected via dependency injection but with the user of Fakes, it can be done directly in code. TBD
References
Types of Asserts
https://msdn.microsoft.com/en-us/library/ms182530.aspx
Microsoft Fakes
https://msdn.microsoft.com/en-us/library/hh549175.aspx
Test Driven Development
https://msdn.microsoft.com/en-us/library/hh212233.aspx